package org.new9uy.tetris.model.tetrimino;

import java.awt.Color;
import org.new9uy.tetris.model.Matrix;
import org.new9uy.tetris.model.Mino;
import org.new9uy.tetris.util.ArrayUtils;

public class Tetrimino
    implements Cloneable
{
    private Mino[] tetrimino;
    private Color tetriminoColor;
    private int orientation;

    public Tetrimino(Mino[] tetrimino, Color tetriminoColor)
    {
        this.tetrimino = tetrimino;
        this.tetriminoColor = tetriminoColor;
        this.orientation = 0;
    }

    @Override
    public Object clone()
    {
        return new Tetrimino(ArrayUtils.duplicate(getTetrimino()), getColor());
    }

    public final Color getColor()
    {
        return tetriminoColor;
    }

    public Mino refer()
    {
        return mino(2);
    }

    /**
     *
     * @return Number of maximum possible orientation
     */
    public int numRotates()
    {
        return 4;
    }

    //Convention: Le "centre" est le point inferieur-gauche de ce Mino:
    public Mino center(Matrix mtx)
    {
        if (orientation == 0)
        {
            return refer();
        } else
        {
            if (orientation == 1)
            {
                return mtx.getMino(refer().getX(), refer().getY() + 1);
            } else
            {
                if (orientation == 2)
                {
                    return mtx.getMino(refer().getX() + 1, refer().getY() + 1);
                } else
                {
                    if (orientation == 3)
                    {
                        return mtx.getMino(refer().getX() + 1, refer().getY());
                    } else
                    {
                        throw new java.lang.IllegalStateException(
                                "orientation cannot be equal to: " + orientation);
                    }
                }
            }
        }
    }

    public Mino[] rotate(Matrix mtx)
    {
        Mino center = center(mtx);
        Mino[] ret = new Mino[4];
        int x, y, x2, y2;

        for (int i = 0; i < 4; i++)
        {
            x = tetrimino[i].getX() - center.getX();
            y = tetrimino[i].getY() - center.getY();
            //90� Rotation (rotation matrix):
            x2 = y;
            y2 = -x;

            ret[i] = mtx.getMino(x2 + center.getX(), y2 + center.getY() - 1);
        }

        return ret;
    }

    //USE WITH EXTREME CAUTION!!
    //The param tetrimino must respect the appropriate form. Improper usage of this
    //method will cause rotate() and other methods to malfunction.
    public void setTetrimino(Mino[] tetrimino)
    {
        this.tetrimino = tetrimino;
    }

    public void updateOrientation()
    {
        orientation = (orientation + 1) % 4;
    }

    public int getOrientation()
    {
        return orientation;
    }

    public Mino[] getTetrimino()/////////////////
    {
        return tetrimino;
    }

    public Mino mino(int i)
    {
        return tetrimino[i];
    }

    @Override
    public String toString()
    {
        return this.getClass().getSimpleName() + ": "
                + mino(0).coordinates() + " "
                + mino(1).coordinates() + " "
                + mino(2).coordinates() + " "
                + mino(3).coordinates();
    }

    public int searchMino(String position)
    {
        int i = 0;
        int pos = 0;

        if (position.equals("MaxX"))
        {
            while (i != 4)
            {
                if (tetrimino[pos].getX() < tetrimino[i].getX())
                {
                    pos = i;
                }
                i++;
            }
        } else
        {
            if (position.equals("MaxY"))
            {
                while (i != 4)
                {
                    if (tetrimino[pos].getY() < tetrimino[i].getY())
                    {
                        pos = i;
                    }
                    i++;
                }
            } else
            {
                if (position.equals("MinX"))
                {
                    while (i != 4)
                    {
                        if (tetrimino[pos].getX() > tetrimino[i].getX())
                        {
                            pos = i;
                        }
                        i++;
                    }
                } else
                {
                    if (position.equals("MinY"))
                    {
                        while (i != 4)
                        {
                            if (tetrimino[pos].getY() > tetrimino[i].getY())
                            {
                                pos = i;
                            }
                            i++;
                        }
                    } else
                    {
                        throw new IllegalArgumentException(
                                "The value " + position + " for the argument position is "
                                + "cannot be recognised.");
                    }
                }
            }
        }

        return pos;
    }

    public Mino[] lowests()
    {
        Mino[] all = new Mino[4];
        int x = tetrimino[searchMino("MinX")].getX(),
                n = 0;

        for (int i = 0; i < 4; i++)
        {
            Mino lowest = lowestInX(x);
            if (lowest != null)
            {
                all[n++] = lowest;
            }
            x++;
        }

        return all.clone();
    }

    private Mino lowestInX(int x)
    {
        Mino lowest = null;
        for (int i = 0; i < 4; i++)
        {
            if (tetrimino[i].getX() == x)
            {
                if (lowest == null || (lowest.getY() > tetrimino[i].getY()))
                {
                    lowest = tetrimino[i];
                }
            }
        }
        return lowest;
    }
}
